//
//	License: The MIT License (MIT)
//  Copyright (c) 2010 Kai Bidstrup 
//
//	http://ua6281.codeplex.com/
//

using System;
using System.Text;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using System.Collections;

namespace A6281 {
	public class Multiple : IDisposable {

		protected SPI.Configuration configuration;
		protected SPI serialPeripheralInterface;
		protected OutputPort latchOutput;
		protected OutputPort outputEnableOutput;
		protected object registersSyncObject = new object();
		protected ChipRegisters[] registers;
		protected object runningSequenceSyncObject = new object();
		protected bool runningSequence;

		public ChipRegisters[] Registers {
			get {
				lock ( this.registersSyncObject ) {
					return this.registers;
				}
			}
			protected set {
				lock ( this.registersSyncObject ) {
					this.registers = value;
				}
			}
		}

		public bool RunningSequence {
			get {
				lock ( this.runningSequenceSyncObject ) {
					return this.runningSequence;
				}
			}
			set {
				lock ( this.runningSequenceSyncObject ) {
					this.runningSequence = value;
				}
			}
		}

		protected Multiple( Cpu.Pin chipSelectPin, Cpu.Pin latchPin, Cpu.Pin outputEnablePin, SPI.SPI_module spiModule ) {
			try {
				this.configuration = new SPI.Configuration( chipSelectPin
					, true
					, 1
					, 1
					, false
					, true
					, 200
					, spiModule );

				this.latchOutput = new OutputPort( latchPin, false );
				this.latchOutput.Write( false );

				this.outputEnableOutput = new OutputPort( outputEnablePin, false );
				this.outputEnableOutput.Write( false );

				this.serialPeripheralInterface = new SPI( this.configuration );
			}
			catch ( Exception ex ) {
				Debug.Print( ex.Message );
				throw ex;
			}
		}

		public Multiple( int daisyChainedModuleCount, Cpu.Pin chipSelectPin, Cpu.Pin latchPin, Cpu.Pin outputEnablePin, SPI.SPI_module spiModule ) :
				this( chipSelectPin, latchPin, outputEnablePin, spiModule ) {
			
			try {
				this.registers = new ChipRegisters[ daisyChainedModuleCount ];
				for ( int index = 0; index < this.registers.Length; index++ )
					this.registers[ index ] = new ChipRegisters();

			}
			catch ( Exception ex ) {
				Debug.Print( ex.Message );
				throw ex;
			}
		}

		public Multiple( int daisyChainedModuleCount, Cpu.Pin chipSelectPin, Cpu.Pin latchPin, Cpu.Pin outputEnablePin, SPI.SPI_module spiModule, ColorOrder colorOrder ) :
			this( chipSelectPin, latchPin, outputEnablePin, spiModule ) {

			try {
				this.registers = new ChipRegisters[ daisyChainedModuleCount ];
				for ( int index = 0; index < this.registers.Length; index++ )
					this.registers[ index ] = new ChipRegisters( colorOrder );

			}
			catch ( Exception ex ) {
				Debug.Print( ex.Message );
				throw ex;
			}
		}

		public bool On {
			get {
				return !this.outputEnableOutput.Read();
			}
			set {
				this.outputEnableOutput.Write( !value );
			}
		}

		public void SetCorrectionImmediate() {
			this.SetImmediate( false );
		}

		public void SetColorImmediate() {
			this.SetImmediate( true );
		}

		public void SetColorImmediate( LightSet lightSet ) {
			lock ( registersSyncObject ) {
				for ( int index = 0; index < lightSet.Colors.Length && index < this.Registers.Length; index++ ) {
					this.Registers[ index ].Color = lightSet.Colors[ index ];
				}
				this.SetColorImmediate();
			}
		}

		public void SetColorImmediate( ChipColor singleColor ) {
			lock ( registersSyncObject ) {
				for ( int index = 0; index < this.Registers.Length; index++ ) {
					this.Registers[ index ].Color = singleColor;
				}
				this.SetColorImmediate();
			}
		}

		public void SetColorImmediate( int module, ChipColor singleColor ) {
			lock ( registersSyncObject ) {
				this.Registers[ module ].Color = singleColor;
				this.SetColorImmediate();
			}
		}

		public void SetColorImmediate( LightSequence lightSequence ) {
			try {
				this.RunningSequence = true;
				lock ( registersSyncObject ) {
					for ( int index = 0; index < lightSequence.LightSets.Length && this.RunningSequence; index++ ) {
						this.SetColorImmediate( lightSequence.LightSets[ index ] );
						//this.SetCorrectionImmediate();
						Thread.Sleep( lightSequence.LightSets[ index ].DisplayTimeInMilliSeconds );
					}
				}
			}
			catch ( Exception ) {
				throw;
			}
			finally {
				this.RunningSequence = false;
			}
		}

		public void SetColorImmediate( ChipColorSet chipColorSet ) {
			IEnumerator chipColorEnumerator = chipColorSet.GetEnumerator();
			lock ( registersSyncObject ) {
				for ( int index = 0; index < this.Registers.Length; index++ ) {
					ChipColor chipColor = ( ChipColor )chipColorEnumerator.Current;
					this.Registers[ index ].Color = chipColor;
					chipColorEnumerator.MoveNext();
				}
			}
			this.SetColorImmediate();
		}

		public void RunChipColorSequence( ChipColorSequence colorSequence, DelaySet delaySet ) {
			try {
				this.RunningSequence = true;
				// run in loop, only stop on !RunningSequence 
				IEnumerator colorSetEnumerator = colorSequence.GetEnumerator();
				IEnumerator delaySetEnumerator = delaySet.GetEnumerator();

				while ( this.RunningSequence ) {
					Delay delay = ( Delay )delaySetEnumerator.Current;
					ChipColorSet chipColorSet = ( ChipColorSet )colorSetEnumerator.Current;

					this.SetColorImmediate( chipColorSet );

					colorSetEnumerator.MoveNext();
					delaySetEnumerator.MoveNext();
					delay.Execute();
				}
			}
			catch ( Exception ) {
				throw;
			}
			finally {
				this.RunningSequence = false;
			}
		}

		public void RunChipColorSequence( ChipColorSequence colorSequence, DelaySet delaySet, TimeSpan timeSpanRunUntil ) {
			try {
				this.RunningSequence = true;
				DateTime until = DateTime.Now + timeSpanRunUntil;

				IEnumerator colorSetEnumerator = colorSequence.GetEnumerator();
				IEnumerator delaySetEnumerator = delaySet.GetEnumerator();

				while ( until > DateTime.Now && this.RunningSequence ) {
					Delay delay = ( Delay )delaySetEnumerator.Current;
					ChipColorSet chipColorSet = ( ChipColorSet )colorSetEnumerator.Current;

					this.SetColorImmediate( chipColorSet );

					colorSetEnumerator.MoveNext();
					delaySetEnumerator.MoveNext();
					delay.Execute();
				}
			}
			catch ( Exception ) {
				throw;
			}
			finally {
				this.RunningSequence = false;
			}
		}

		#region private members

		private void SetImmediate( bool dataNotCorrection ) {
			try {
				lock ( this.registersSyncObject ) {
					byte[] packet = new byte[ this.Registers.Length * ChipRegisters.PacketSize ];

					for ( int chipIndex = 0; chipIndex < this.Registers.Length; chipIndex++ ) {

						// reverse the packet order because first is routed to the last module
						int packetOffset = ChipRegisters.PacketSize * ( this.Registers.Length - chipIndex - 1 );

						if ( dataNotCorrection )
							this.Registers[ chipIndex ].SetDataColorPacket( packet, packetOffset );
						else
							this.Registers[ chipIndex ].SetDataCorrectionPacket( packet, packetOffset );
					}

					this.serialPeripheralInterface.Write( packet );
				}
				//Thread.Sleep( latchDelay );
				Thread.Sleep( 1 );
				this.latchOutput.Write( true );
				Thread.Sleep( 1 );
				//Thread.Sleep( latchDelay );
				this.latchOutput.Write( false );
			}
			catch ( Exception ex ) {
				Debug.Print( ex.Message );
				throw ex;
			}
		}

		#endregion
		#region IDisposable Members

		public void Dispose() {
			this.outputEnableOutput.Dispose();
			this.outputEnableOutput = null;
			this.latchOutput.Dispose();
			this.latchOutput = null;
			this.serialPeripheralInterface.Dispose();
			this.serialPeripheralInterface = null;
			GC.SuppressFinalize( this );
		}

		#endregion
	}
}
